Skip to content

Conversation

stephentyrone
Copy link
Contributor

@stephentyrone stephentyrone commented Oct 2, 2025

Adapts Date to use a double-Double representation (the underlying representation is the sum of two double-precision values, giving about 106 bits of precision).

Previously Date was backed by a single Double measuring time since Jan 1 2001 in seconds. Because Double's precision is non-uniform, this means that times within a hundred days of the epoch are represented with approximately nanosecond precision, but as you get farther away from that date the precision decreases. For times close to today, accuracy has been reduced to about 100ns.

The obvious thing would be to adopt an integer-based representation similar to C's timespec (32b nanoseconds, 64b seconds) or Swift's Duration (128b attoseconds). These representations suffer from a few difficulties:

  • Existing API on Date takes and produces TimeInterval (aka Double). Making Date use an integer representation internally would mean that existing users of the public API would suddently start getting different results for computations that were previously exact; even though we could add new API, and the overall system would be more precise, this would be a surprisingly subtle change for users to navigate.

  • We have been told that some software interprets the raw bytes of Date as a Double for the purposes of fast serialization. These packages formally violate Foundation's API boundaries, but that doesn't help users of those packages who would abruptly be broken by switching to an integer representation.

Using DoubleDouble instead navigates these problems fairly elegantly.

  • Because DoubleDouble is still a floating-point type, it still suffers from non-uniform precision. However, because DoubleDouble is so fantastically precise, it can represent dates out to ±2.5 quadrillion years at ~nanosecond or better precision, so in practice this won't be much of an issue.

  • Existing API on Date will produce exactly the same result as it did previously in cases where those results were exact, minimizing surprises. In cases where the existing API was not exact, it will produce much more accurate results, even if users do not adopt new API, because its internal calculations are now more precise.

  • Software that (incorrectly) interprets the raw bytes of Date as a Double will get at least as accurate of a value as it did previously (and often a more accurate value).

DateInterval gets the same treatment, so that it can benefit from more accurate internal computations as well. Follow-on work may adapt/add-to Date's API to make it easier to specify Dates with high precision.

Adapts Date to use a double-Double representation (the underlying representation is the sum of two double-precision values, giving about 106 bits of precision). Keeping a binary floating-point representation means that existing API that converts Date to/from Double values will not see a difference in behavior for simple cases (it will, however, become more accurate in some other cases). Every date computation that was previously exact is still exact. Many cases that previously might have rounded will now generate exact results.

DateInterval gets the same treatment; both its start Date and duration are represented as double-Double quantities with this change.

If we move forward with this change, we'll investigate adding new APIs to make it more convenient to specify high-precision Dates; this change merely makes it possible to do so.
@stephentyrone stephentyrone marked this pull request as draft October 2, 2025 15:47
@stephentyrone
Copy link
Contributor Author

Converted to draft as I want to add some additional comments and tests before this moves forward, but the basic shape of the change is correct, I think.

@parkera
Copy link
Contributor

parkera commented Oct 3, 2025

@swift-ci test

Previously these were disabled because of an API availability issue in 5.8/5.9; we're no longer developing for those targets, so let's re-enable these tests.
Also add some other documentation comments on the internal DoubleDouble type and rename init(head:tail:) to init(uncheckedHead:tail:) to better document that its precondition is not enforced in Release builds
@stephentyrone stephentyrone marked this pull request as ready for review October 8, 2025 19:13
@stephentyrone
Copy link
Contributor Author

@swift-ci test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants